package jalgebrava.util.collections;

import static jalgebrava.util.Pair.*;
import jalgebrava.util.Pair;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class IterableUtil {
	/**
	 * The range of integers starting at from, counting up, to the element before upTo.
	 * @param from
	 * @param upTo
	 * @return a stream of integers
	 */
	public final static ImmutableIterable<Integer> integerRange(final int from, final int upTo) {
		return new ImmutableIterable<Integer>() {

			@Override
			public ImmutableIterator<Integer> iterator() {
				return new ImmutableIterator<Integer>() {

					private int current = from;
					@Override
					public boolean hasNext() {
						return current < upTo; 
					}

					@Override
					public Integer next() {
						return current++;
					}
					
				};
			}
			
		};
	}
	public final static <T> ImmutableIterator<T> singletonIterator(final T t) {
		return new ImmutableIterator<T>() {
			private boolean nextCalled = false;
			@Override
			public boolean hasNext() {
				return !nextCalled; 
			}

			@Override
			public T next() {
				
				if (nextCalled) {
					return null;
				}
				nextCalled = true;
				return t;
			}
			
		};
	}
	public final static <T> ImmutableIterable<T> singletonIterable(final T t) {
		return new ImmutableIterable<T>() {

			@Override
			public ImmutableIterator<T> iterator() {
				return singletonIterator(t);
			}
		};
	}
	public static<A> ImmutableIterable<A> emptyIterable() {
		return new ImmutableIterable<A>() {

			@Override
			public ImmutableIterator<A> iterator() {
				return new ImmutableIterator<A>() {

					@Override
					public boolean hasNext() {						
						return false;
					}

					@Override
					public A next() {
						throw new NoSuchElementException("Empty iterable");
					}
					
				};
			}
			
		};
	}
	
	public static final <A,B> ImmutableIterable<Pair<A,B>> cartesianProduct(final Iterable<A> as, final Iterable<B> bs) {
		return new ImmutableIterable<Pair<A,B>>() {

			@Override
			public ImmutableIterator<Pair<A,B>> iterator() {
				return new ImmutableIterator<Pair<A,B>>() {

					private final Iterator<A> itA = as.iterator();
					private A lastA;
					
					private Iterator<B> itB = bs.iterator();
					
					@Override
					public boolean hasNext() {
						return itA.hasNext() || itB.hasNext();
					}

					@Override
					public Pair<A,B> next() {
						if (lastA == null) {
							lastA = itA.next();
						}
						if (itB.hasNext()) {
							return pair(lastA, itB.next());
						}
						// reset itH;
						itB = bs.iterator();
						lastA = itA.next();						
						return pair(lastA, itB.next());
					}
					
				};
			}

			
		};	
	}
}
